import json
import time
import threading
import re
from flask import Flask, jsonify, render_template, request, redirect, url_for


def load_config(path: str = "config.json") -> dict:
    try:
        with open(path, "r", encoding="utf-8") as f:
            raw = json.load(f)
    except Exception:
        raw = {}

    # 提供合理默认值，确保文件缺失或字段缺失时仍可启动
    return {
        "host": raw.get("host", "127.0.0.1"),
        "port": int(raw.get("port", 5000)),
        "debug": bool(raw.get("debug", True)),
    }


app = Flask(__name__)

# 确保JSON序列化不使用ASCII转义，直接输出UTF-8
try:
    app.json.ensure_ascii = False
except Exception:
    # 兼容旧版本Flask：忽略不可用属性
    pass


@app.after_request
def _force_utf8_json(response):
    ct = response.headers.get("Content-Type", "")
    if ct.startswith("application/json") and "charset" not in ct.lower():
        response.headers["Content-Type"] = "application/json; charset=utf-8"
    return response


# 简单的每接口限流（按客户端IP + 路径），默认每3秒一次
LAST_ACCESS: dict[tuple[str, str], float] = {}
ACCESS_LOCK = threading.Lock()
RATE_LIMIT_SECONDS = 3


def _client_id() -> str:
    forwarded = request.headers.get("X-Forwarded-For")
    if forwarded:
        return forwarded.split(",")[0].strip()
    return request.remote_addr or "unknown"


@app.before_request
def _rate_limit():
    # 仅对 /api/* 路由进行限流
    if request.path.startswith("/api/"):
        key = (_client_id(), request.path)
        now = time.monotonic()
        with ACCESS_LOCK:
            last = LAST_ACCESS.get(key, 0.0)
            if now - last < RATE_LIMIT_SECONDS:
                remaining = RATE_LIMIT_SECONDS - (now - last)
                return jsonify({
                    "status": "error",
                    "message": f"请求过于频繁，请 {remaining:.1f} 秒后再试",
                    "endpoint": request.path,
                }), 429
            LAST_ACCESS[key] = now


# 伪造的登录数据，供校验使用
FAKE_USER = {"username": "admin", "password": "123456"}


@app.get("/")
def index():
    return redirect(url_for("login"))


@app.get("/login")
def login():
    return render_template("login.html", error=None)


@app.post("/login")
def login_post():
    username = request.form.get("username", "").strip()
    password = request.form.get("password", "")

    if username == FAKE_USER["username"] and password == FAKE_USER["password"]:
        return redirect(url_for("login_success", username=username))

    return render_template("login.html", error="用户名或密码错误")


@app.get("/success")
def login_success():
    username = request.args.get("username", FAKE_USER["username"])
    return render_template("success.html", username=username)


# ============================
# Meraki 风格：模拟数据与GET接口
# ============================

# 组织
ORGANIZATIONS = [
    {"id": "org_1", "name": "Demo Organization", "url": "https://dashboard.meraki.com/o/org_1"}
]

# 网络（按组织）
NETWORKS = {
    "org_1": [
        {"id": "net_1", "name": "HQ", "productTypes": ["appliance", "switch", "wireless"], "timezone": "Asia/Shanghai"},
        {"id": "net_2", "name": "Branch", "productTypes": ["appliance", "wireless"], "timezone": "Asia/Shanghai"},
    ]
}

# 设备（按网络）
DEVICES = {
    "net_1": [
        {"serial": "Q2XX-AAAA-1111", "model": "MX68", "mac": "aa:bb:cc:dd:ee:01", "name": "Security Appliance"},
        {"serial": "Q2XX-BBBB-2222", "model": "MS120-8", "mac": "aa:bb:cc:dd:ee:02", "name": "Access Switch"},
        {"serial": "Q2XX-CCCC-3333", "model": "MR42", "mac": "aa:bb:cc:dd:ee:03", "name": "Office AP"},
    ],
    "net_2": [
        {"serial": "Q2YY-DDDD-4444", "model": "MX64", "mac": "aa:bb:cc:dd:ee:04", "name": "Branch Appliance"},
        {"serial": "Q2YY-EEEE-5555", "model": "MR33", "mac": "aa:bb:cc:dd:ee:05", "name": "Branch AP"},
    ],
}

# VLAN（按网络）
VLANs = {
    "net_1": [
        {"id": "10", "name": "Users", "subnet": "10.0.10.0/24", "applianceIp": "10.0.10.1"},
        {"id": "20", "name": "Voice", "subnet": "10.0.20.0/24", "applianceIp": "10.0.20.1"},
    ],
    "net_2": [
        {"id": "30", "name": "Branch-Users", "subnet": "10.0.30.0/24", "applianceIp": "10.0.30.1"}
    ],
}

# SSID（按网络）
SSIDs = {
    "net_1": [
        {"number": 0, "name": "Corp", "enabled": True},
        {"number": 1, "name": "Guest", "enabled": True},
    ],
    "net_2": [
        {"number": 0, "name": "Branch-Corp", "enabled": True},
        {"number": 1, "name": "Branch-Guest", "enabled": False},
    ],
}

# Ports 示例（按网络）
APPLIANCE_PORTS = {
    "net_1": [
        {"name": "WAN1", "status": "up", "ip": "100.64.1.10"},
        {"name": "LAN1", "status": "up", "vlan": "10"},
        {"name": "LAN2", "status": "up", "vlan": "20"},
    ],
    "net_2": [
        {"name": "WAN1", "status": "up", "ip": "100.64.2.10"},
        {"name": "LAN1", "status": "up", "vlan": "30"},
    ],
}

SWITCH_PORTS = {
    "net_1": [
        {"port": 1, "status": "up", "vlan": 10},
        {"port": 2, "status": "up", "vlan": 20},
        {"port": 3, "status": "down", "vlan": None},
        {"port": 4, "status": "up", "vlan": 10},
    ],
    "net_2": [
        {"port": 1, "status": "up", "vlan": 30},
        {"port": 2, "status": "down", "vlan": None},
    ],
}


def _json_404(message: str):
    return jsonify({"status": "error", "message": message}), 404


# Base path 尝试对齐 Meraki：/api/v1
@app.get("/api/v1/organizations")
def meraki_organizations():
    return jsonify(ORGANIZATIONS)


@app.get("/api/v1/organizations/<org_id>/networks")
def meraki_networks(org_id: str):
    networks = NETWORKS.get(org_id)
    if networks is None:
        return _json_404(f"组织 {org_id} 不存在或无网络")
    return jsonify(networks)


@app.get("/api/v1/networks/<network_id>/devices")
def meraki_devices(network_id: str):
    devices = DEVICES.get(network_id)
    if devices is None:
        return _json_404(f"网络 {network_id} 不存在或无设备")
    return jsonify(devices)


@app.get("/api/v1/networks/<network_id>/vlans")
def meraki_vlans(network_id: str):
    vlans = VLANs.get(network_id)
    if vlans is None:
        return _json_404(f"网络 {network_id} 不存在或无VLAN")
    return jsonify(vlans)


@app.get("/api/v1/networks/<network_id>/ssids")
def meraki_ssids(network_id: str):
    ssids = SSIDs.get(network_id)
    if ssids is None:
        return _json_404(f"网络 {network_id} 不存在或无SSID")
    return jsonify(ssids)


# 产品/服务路径示例（示意：通过 query 传 networkId）
@app.get("/api/v1/appliance/ports")
def meraki_appliance_ports():
    network_id = request.args.get("networkId")
    if not network_id:
        return _json_404("缺少参数 networkId")
    ports = APPLIANCE_PORTS.get(network_id)
    if ports is None:
        return _json_404(f"网络 {network_id} 不存在或无appliance端口")
    return jsonify(ports)


@app.get("/api/v1/switch/ports")
def meraki_switch_ports():
    network_id = request.args.get("networkId")
    if not network_id:
        return _json_404("缺少参数 networkId")
    ports = SWITCH_PORTS.get(network_id)
    if ports is None:
        return _json_404(f"网络 {network_id} 不存在或无switch端口")
    return jsonify(ports)


# ---------------------------
# CSV驱动的动态GET Mock支持
# ---------------------------
MOCK_INDEX = None
MOCK_PATTERNS = []


def _load_mock_index():
    global MOCK_INDEX, MOCK_PATTERNS
    try:
        with open("mock_meraki_get_endpoints.json", "r", encoding="utf-8") as f:
            MOCK_INDEX = json.load(f)
            MOCK_PATTERNS = [
                (re.compile(ep["pathPattern"]), ep)
                for ep in MOCK_INDEX.get("endpoints", [])
                if ep.get("method") == "get" and ep.get("pathPattern")
            ]
    except Exception:
        MOCK_INDEX = None
        MOCK_PATTERNS = []


_load_mock_index()


@app.get("/api/v1/__index__/get")
def meraki_mock_get_index():
    if not MOCK_INDEX:
        return jsonify({"status": "error", "message": "Mock索引未加载，请先运行生成脚本。"}), 500
    # 仅返回关键信息，避免响应过大
    slim = [{
        "path": ep["path"],
        "operationId": ep.get("operationId"),
        "summary": ep.get("summary"),
    } for ep in MOCK_INDEX.get("endpoints", [])]
    return jsonify({"count": len(slim), "items": slim})


@app.get("/api/v1/<path:subpath>")
def meraki_dynamic_mock(subpath: str):
    # 已存在的具体路由优先生效，此处作为兜底动态匹配
    if not MOCK_PATTERNS:
        return _json_404("未加载CSV Mock索引")
    # 去掉可能的前导斜杠
    subpath = subpath.lstrip('/')
    for pattern, ep in MOCK_PATTERNS:
        if pattern.match(subpath):
            data = {
                "status": "ok",
                "path": "/" + subpath,
                "operationId": ep.get("operationId"),
                "summary": ep.get("summary"),
                "mock": ep.get("mock", {}),
            }
            return jsonify(data)
    return _json_404(f"未找到匹配的Mock：/{subpath}")


@app.get("/api/foo")
def foo():
    return jsonify({
        "status": "ok",
        "endpoint": "foo",
        "items": [1, 2, 3],
        "message": "固定JSON返回示例",
    })


@app.get("/api/bar")
def bar():
    return jsonify({
        "status": "ok",
        "endpoint": "bar",
        "user": {"id": 123, "name": "Alice"},
        "message": "另一组固定JSON返回示例",
    })


if __name__ == "__main__":
    cfg = load_config()
    RATE_LIMIT_SECONDS = int(cfg.get("rate_limit_seconds", 3))
    app.run(host=cfg["host"], port=cfg["port"], debug=cfg["debug"])
else:
    # 使得被WSGI加载时也可读取Mock索引
    pass
    